package retrofit.boot.apache.autoconfigure;

import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.springframework.context.annotation.Import;
import retrofit.boot.autoconfigure.RetrofitAutoConfiguration;
import retrofit.boot.client.IRetrofitClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import retrofit.boot.apache.client.ApacheHttpClient;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Configuration
@ConditionalOnClass({HttpClient.class})
@EnableConfigurationProperties({HttpClientProperties.class})
@Import({RetrofitAutoConfiguration.class})
public class HttpClientAutoConfiguration {

    private static ScheduledExecutorService httpIdleConnectionMonitor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("http-idleConnection-monitor",true));

    private final HttpClientProperties properties;

    private final HttpClient httpClient;

    public HttpClientAutoConfiguration(HttpClientProperties properties) {
        this.properties = properties;
        httpClient = HttpClients.custom()
                .setConnectionManager(this.poolingHttpClientConnectionManager(properties))
                .setDefaultRequestConfig(this.getRequestConfig())
                .build();
    }

    @Bean
    public IRetrofitClient retrofitClient(){
        return new ApacheHttpClient(httpClient,this.getRequestConfig());
    }

    private PoolingHttpClientConnectionManager poolingHttpClientConnectionManager(HttpClientProperties properties){
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory()).build();
        PoolingHttpClientConnectionManager manager  = new PoolingHttpClientConnectionManager(registry);
        manager.setDefaultMaxPerRoute(properties.getDefaultMaxPerRoute());
        manager.setMaxTotal(properties.getMaxTotal());
        //开启空闲链接监控线程
        httpIdleConnectionMonitor.scheduleAtFixedRate(() -> {
            manager.closeExpiredConnections(); // 关闭失效的连接
            manager.closeIdleConnections(properties.getIdleTimeout(), TimeUnit.MILLISECONDS);// 关闭不活动的连接
        },10L,5L,TimeUnit.SECONDS);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            httpIdleConnectionMonitor.shutdown();
            manager.shutdown();
        }));
        return manager;
    }

    private RequestConfig getRequestConfig(){
        return RequestConfig.custom()
                .setConnectionRequestTimeout(properties.getConnectionRequestTimeout())
                .setConnectTimeout(properties.getConnectTimeout())
                .setSocketTimeout(properties.getSocketTimeout())
                .build();
    }

    private static class NamedThreadFactory implements ThreadFactory {

        protected static final AtomicInteger POOL_SEQ = new AtomicInteger(1);

        protected final AtomicInteger mThreadNum = new AtomicInteger(1);

        protected final String mPrefix;

        protected final boolean mDaemon;

        protected final ThreadGroup mGroup;

        public NamedThreadFactory() {
            this("pool-" + POOL_SEQ.getAndIncrement(), false);
        }

        public NamedThreadFactory(String prefix) {
            this(prefix, false);
        }

        public NamedThreadFactory(String prefix, boolean daemon) {
            mPrefix = prefix + "-thread-";
            mDaemon = daemon;
            SecurityManager s = System.getSecurityManager();
            mGroup = (s == null) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
        }

        @Override
        public Thread newThread(Runnable runnable) {
            String name = mPrefix + mThreadNum.getAndIncrement();
            Thread ret = new Thread(mGroup, runnable, name, 0);
            ret.setDaemon(mDaemon);
            return ret;
        }
        public ThreadGroup getThreadGroup() {
            return mGroup;
        }
    }
}
